package com.math.util;

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.List;

import com.database.bean.Product;
import com.database.bean.Setting;

public class CalculationUtil {
	
	/**
	 * returns the utility value of an agent based on his weightings of the difference factors as well as the current values of the product or firm that produces that product
	 * @param agentPreferencePrice - weighting based on preferences for price
	 * @param weightingQuality - weighting based on preferences for Quality
	 * @param weightingImage - weighting based on preferences for Image
	 * @param referenceValuePrice - value between 0 and 1 (min price of all products / current product)
	 * @param qualityValue - value between 0 and 1 of the Firm
	 * @param imageValue - value between 0 and 1 of the Firm
	 * @return
	 */
	public static double calculateValue(double agentPreferencePrice,double agentPreferenceQuality,double agentPreferenceImage,double referenceValuePrice,double qualityValue,double imageValue,Setting setting){
		
		double sumPreferences=agentPreferencePrice+agentPreferenceQuality+agentPreferenceImage;
		
		double weightingPrice=agentPreferencePrice/sumPreferences;
		double weightingQuality=agentPreferenceQuality/sumPreferences;
		double weightingImage=agentPreferenceImage/sumPreferences;
		
//		System.out.println("weightingPrice: "+weightingPrice);
//		System.out.println("weightingQuality: "+weightingQuality);
//		System.out.println("weightingImage: "+weightingImage);
		
		double utilityPrecision=0.01;
		if(setting.getUtilityPrecision()!=null)
			utilityPrecision=setting.getUtilityPrecision();
		
		double utility = (weightingPrice*referenceValuePrice)+(weightingQuality*qualityValue)+(weightingImage*imageValue);
		double roundedUtility=roundToNearest(utility,utilityPrecision);
		return roundedUtility;
	}
	
	public static double roundDouble(double value,int scale){
		BigDecimal val = new BigDecimal(value + "");
	    return val.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}
	
	public static double roundToNearest(double d, double toNearest) {
	    if (toNearest <= 0) {
	        throw new IllegalArgumentException(
	                "toNearest must be positive, encountered " + toNearest);
	    }
	    double rounded=Math.round(d/toNearest) * toNearest;
	    return roundDouble(rounded,2);
	}
	
//	public static double roundDoublePrecision(double value,int precision){
//		long intValue = Math.round(value);
//		BigDecimal val = new BigDecimal(intValue + "");
//		MathContext mc = new MathContext(precision);
//	    return val.round(mc).doubleValue();
//	}
	
	public static double roundDoubleCalcPrecision(double value){
		long intValue = Math.round(value);
		BigDecimal val = new BigDecimal(intValue + "");
		int precision = val.precision();
		if(precision>2){
			precision=precision-2;
		}
		MathContext mc = new MathContext(precision);
	    return val.round(mc).doubleValue();
	}
	
	/**
	 * Calculates the extra costs for a too large sortiment
	 * @param nrProducts
	 */
	public static double calculateLogisticCosts(int nrProducts,Setting setting){
		double logisticCosts = 0.0;
		if(setting!=null){
			int maxNrProducts = setting.getMaxFreePortfolioProducts(); 
			double extraCosts = setting.getPortfolioExtraCostsPerProduct(); 
			double risingFactor= setting.getPortfolioFactor();
			
			//the amount until maxNrProducts includes no extra logistic costs
			if(nrProducts>maxNrProducts){ 
				nrProducts = nrProducts - maxNrProducts;
				double nrProductDividedTen=((double)nrProducts) / risingFactor;
				logisticCosts = Math.exp(nrProductDividedTen) * (nrProducts*extraCosts);
			}
			
			logisticCosts = Math.round(logisticCosts);
		}
		return logisticCosts;
	}
	
	/**
	 * Calculates initial values 
	 * @param products
	 * @param amountCompanies
	 * @param amountAgents
	 * @param setting
	 * @return
	 */
	public static InitializationBean calculateInitialValues(List<Product> products, int amountCompanies, int amountAgents, Setting setting){
		
		InitializationBean result = new InitializationBean();
		
		if(amountCompanies==0){
			return null;
		}
		
		double tradeMargin = setting.getTradeMargin();
		
		// calculation of selling price and profit (price + tradeMargin = selling price)
		// also store the most expensive product for other calculations
		double highestPrice = 0.0;
		Product highestProduct = null;
		for(Product p:products){
			Double possibleProfit = p.getProductCosts()*tradeMargin;
			Double possiblePrice = p.getProductCosts()*(1+tradeMargin);
			p.setPossibleProfit(possibleProfit);
			p.setPossiblePrice(possiblePrice);
			
			if(highestPrice<p.getPossiblePrice()){
				highestPrice = p.getPossiblePrice();
				highestProduct = p;
			}
		}
		
		double highestProfit=0.0;
		double highestRevenue=0.0;
		//calculation of highest possible revenue and profit
		if(highestProduct!=null){
			highestProfit = highestProduct.getPossibleProfit() * amountAgents / amountCompanies;
			highestRevenue = highestProduct.getPossiblePrice() * amountAgents / amountCompanies;
		}
		
		result.setSumProfit(highestProfit);
		result.setSumRevenue(highestRevenue);
		
		//amount agents/amount companies = products/company 
		//companyrevenue * Marketing%,RD%,Personal% for maximal investmentcosts for companies to choose
		result.setPercentageMarketing(setting.getFactorCostMarketing());
		result.setPercentagePersonal(setting.getFactorCostPersonal());
		result.setPercentageRD(setting.getFactorCostRD());
		
		//companyrevenue * Logistic% for logisticCosts
		result.setLogisticCostsPercentage(setting.getFactorLogisticCosts());
		
		//companyrevenue * Report% for report costs
		//setter
		result.setReportBigPercentage(setting.getFactorReportBig());
		result.setReportMiddlePercentage(setting.getFactorReportMiddle());
		result.setReportSmallPercentage(setting.getFactorReportSmall());
		result.setReportSpyPercentage(setting.getFactorReportSpy());
		
		result.setAmountAgents(amountAgents);
		result.setAmountCompanies(amountCompanies);
		result.setFactorAgents(setting.getAgentAmountCompanyFactor());
		
		result.setStartBudgetPercentage(setting.getFactorStartBudget());
		//calculate initial costs out of revenue and different percentage factors
		result.calculateCosts();
		
		return result;
	}
	
	/**
	 * Calculates initial values 
	 * @param products
	 * @param amountCompanies
	 * @param amountAgents
	 * @param setting
	 * @return
	 */
	public static InitializationBean calculateInitialValuesWithAgentGroups(List<Product> products, int amountCompanies, int amountAgents, Setting setting){

		InitializationBean result = new InitializationBean();
		
		double factorLowAgents = 0.35;
		double factorMiddleAgents = 0.50;
		double factorHighAgents = 0.15;
		
		//Agents auf Typen mit Prozent aufschlüsseln (double list, hashmap)
		double lowAgents = amountAgents * factorLowAgents;
		double middleAgents = amountAgents * factorMiddleAgents;
		double highAgents = amountAgents * factorHighAgents;
		
		//0 >= x <= 0,3		0,3 > x <= 0,7			0,7 > x <= 1
		double q1=0.3;
		double q2=0.7;
		List<Product> lowProds = new ArrayList<Product>();
		List<Product> middleProds = new ArrayList<Product>();
		List<Product> highProds = new ArrayList<Product>();
		
		double tradeMargin = 0.3; 
		
		//Produkte laut Qualitätsgrenzen aufteilen (double list, hashmap)
		// - anzahl der verschiedenen produkte in einer qualitätsklasse
		// - berechnung des vorraussichtlichen verkaufspreises (mittels Handelsspanne)
		// - berechnung des vorraussichtlichen gewinns
		for(Product p:products){
			Double qf = p.getQualityFactor();
			Double possibleProfit = p.getProductCosts()*tradeMargin;
			Double possiblePrice = p.getProductCosts()*(1+tradeMargin);
			p.setPossibleProfit(possibleProfit);
			p.setPossiblePrice(possiblePrice);
			
			if(qf<=q1){
				//low
				lowProds.add(p);
			}else if(qf>q1 && qf<=q2){
				//middle
				middleProds.add(p);
			}else{
				//high
				highProds.add(p);
			}
		}
		
		//Anzahl Agents / Anzahl produkte = agents pro produkt (double list, hashmap) 
		lowAgents = lowAgents / lowProds.size();
		middleAgents = middleAgents / middleProds.size();
		highAgents = highAgents / highProds.size();
		
		//multiplikation der agentsanzahl mal gewinn und umsatz
		//Liste mit gewinnen und umsätzen zusammen zählen
		//gesamtumsatz und gesamtgewinn -> Companyumsatz und Companygewinn
		
		Double sumProfit = 0.0;
		Double sumRevenue = 0.0;
		//low
		for(Product p:lowProds){
			sumProfit = sumProfit + p.getPossibleProfit()*lowAgents;
			sumRevenue = sumRevenue + p.getPossiblePrice()*lowAgents;
		}
		//middle
		for(Product p:middleProds){
			sumProfit = sumProfit + p.getPossibleProfit()*middleAgents;
			sumRevenue = sumRevenue + p.getPossiblePrice()*middleAgents;
		}
		//high
		for(Product p:highProds){
			sumProfit = sumProfit + p.getPossibleProfit()*highAgents;
			sumRevenue = sumRevenue + p.getPossiblePrice()*highAgents;
		}
		sumRevenue = sumRevenue / amountCompanies;
		sumProfit = sumProfit / amountCompanies;
		
		//setter
		result.setSumProfit(sumProfit);
		result.setSumRevenue(sumRevenue);
		
		//Anzahl Agents/Anzahl Unternehmen = Anzahl gekaufter Produkte pro unternehmen (also 150)
		//Umsatz * Marketing%,RD%,Personal% für maximale aufwendungskosten für die slider
		Double percentageMarketing = 0.05;
		Double percentageRD = 0.05;
		Double percentagePersonal = 0.05;
		//setter
		result.setPercentageMarketing(percentageMarketing);
		result.setPercentagePersonal(percentagePersonal);
		result.setPercentageRD(percentageRD);
		
		//Umsatz * Logistik% für Logistikkosten
		Double logisticCostsPercentage = 0.01;
		result.setLogisticCostsPercentage(logisticCostsPercentage);
		
		//Umsatz * Berichts% für Berichtskosten
		Double reportSmallPercentage=0.01;
		Double reportMiddlePercentage=0.03;
		Double reportBigPercentage=0.06;
		Double reportSpyPercentage=0.10;
		//setter
		result.setReportBigPercentage(reportBigPercentage);
		result.setReportMiddlePercentage(reportMiddlePercentage);
		result.setReportSmallPercentage(reportSmallPercentage);
		result.setReportSpyPercentage(reportSpyPercentage);
		
		result.setAmountAgents(amountAgents);
		result.setAmountCompanies(amountCompanies);
		
		Double startBudgetPercentage = 0.25;
		result.setStartBudgetPercentage(startBudgetPercentage);
		
		result.calculateCosts();
		
		return result;
	}
	
}
